﻿using System;
using System.Collections.Generic;
using System.Xml;
using System.Xml.Linq;
using System.Globalization;

using XmlVisualizer.Models;
using Tomers.WPF.Localization;

namespace XmlVisualizer.Controllers
{
    /// <summary>
    /// Controller for xml.
    /// </summary>
    public class Controller : IController
    {
        #region Fields
        private IAppModel model; // reference to the model 
        #endregion

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the Controller class, sets model and view, registers view as 
        /// an observer.
        /// </summary>
        /// <param name="model">Reference to the model.</param>
        /// <exception cref="System.ArgumentNullException">When model is null.</exception>
        public Controller(IAppModel model)
        {
            if (model == null)
            {
                throw new ArgumentNullException("model");
            }

            this.model = model;
        }
        #endregion

        #region IController Members

        /// <summary>
        /// New document command.
        /// </summary>
        /// <param name="documentType">Type of document.</param>
        public void NewDocument(Mode documentType)
        {
            switch (documentType)
            {
                case Mode.Xml:
                    Document doc = new Document();
                    doc.ShowDocument();
                    break;
                case Mode.Xsd:

                    break;
                case Mode.Dtd:

                    break;
            }
        }

        /// <summary>
        /// Calls model to open specified file.
        /// </summary>
        /// <param name="filename">File to open.</param>
        public void OpenDocument(string filename)
        {
            this.model.OpenFile(filename);
        }

        /// <summary>
        /// Calls model to save the document to the specified location.
        /// </summary>
        /// <param name="filename">File location where to save the file.</param>
        public void SaveDocument(string filename)
        {
            this.model.SaveFile(filename);
        }

        /// <summary>
        /// Exit method.
        /// </summary>
        public void Exit()
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Changes the culture of the current instance.
        /// </summary>
        /// <param name="lang">Language to change to.</param>
        public void ChangeLanguage(string lang)
        {
            try
            {
                LanguageContext.Instance.Culture = CultureInfo.GetCultureInfo(lang);
                Properties.Settings.Default.language = lang;
                Properties.Settings.Default.Save();
            }
            catch (ArgumentException ex)
            {
                Exceptions.ExceptionHandling.HandleException(ex);
            }
        }

        /// <summary>
        /// Calls model to remove the node.
        /// </summary>
        /// <param name="node">Node to remove.</param>
        /// <param name="recursively">Indicates whether to remove whole tree under the node.</param>
        public void RemoveNode(XNode node, bool recursively)
        {
            this.model.RemoveNode(node, recursively);
        }

        /// <summary>
        /// Calls model to add new element to the specified parent element.
        /// </summary>
        /// <param name="parentElement">Parent element.</param>
        /// <param name="elementToAdd">New element.</param>
        public void AddNewElement(XElement parentElement, XNodeInfo elementToAdd)
        {
            this.model.AddNewElement(parentElement, elementToAdd);
        }

        /// <summary>
        /// Connect two nodes - add childNode as a child of parentNode.
        /// </summary>
        /// <param name="parentNode">Parent node.</param>
        /// <param name="childNode">Child node.</param>
        /// <returns>Returns ConnectResult.</returns>
        public ConnectResult ConnectNodes(XNode parentNode, XNode childNode)
        {
            return this.model.ConnectNodes(parentNode, childNode);
        }

        /// <summary>
        /// Select nodes in the current document.
        /// </summary>
        /// <param name="xpathExpression">XPath expression specifying what to select.</param>
        public void SelectNodes(string xpathExpression)
        {
            this.model.XPathSelection(xpathExpression);
        }

        /// <summary>
        /// When user wants to close the document but not the program.
        /// </summary>
        public void Close()
        {
            this.model.Close();
        }

        /// <summary>
        /// Sets element's name.
        /// </summary>
        /// <param name="element">Element on which to set the name.</param>
        /// <param name="name">The name of the element to set.</param>
        public void SetElementName(XElement element, string name)
        {
            this.model.SetElementName(element, name);
        }

        /// <summary>
        /// Sets element's namespace.
        /// </summary>
        /// <param name="element">Element on which to set the namespace.</param>
        /// <param name="newNamespace">Namespace name.</param>
        public void SetElementNamespace(XElement element, string newNamespace)
        {
           this.model.SetElementNamespace(element, newNamespace);
        }

        /// <summary>
        /// Updates attributes.
        /// </summary>
        /// <param name="element">Element on which to update attributes.</param>
        public void UpdateAttributes(XElement element)
        {
            this.model.UpdateAttributes(element);
        }

        /// <summary>
        /// Prepares for serialization.
        /// </summary>
        public void PrepareForSerialization()
        {
            this.model.AddGuidAttributes();
        }

        /// <summary>
        /// Called after serialization is done.
        /// </summary>
        public void SerializationDone()
        {
            this.model.RemoveGuidAttributes();
        }

        /// <summary>
        /// Sets the mode.
        /// </summary>
        /// <param name="mode">Mode to set.</param>
        public void SetMode(Mode mode)
        {
            this.model.SetMode(mode);
        }

        /// <summary>
        /// Adds information about certain node.
        /// </summary>
        /// <param name="info">XNodeInfo to add.</param>
        public void AddNodeInfo(XNodeInfo info)
        {
            this.model.AddNodeInfo(info);
        }

        /// <summary>
        /// Updates order of elements.
        /// </summary>
        /// <param name="parentElement">Parent element.</param>
        /// <param name="childNodes">Collection of child nodes.</param>
        public void UpdateOrder(XElement parentElement, IEnumerable<XNode> childNodes)
        {
            this.model.UpdateOrder(parentElement, childNodes);
        }

        /// <summary>
        /// Resets the model.
        /// </summary>
        public void ResetModel()
        {
            this.model.Reset();
        }

        /// <summary>
        /// Sets the name of the namespace prefix.
        /// </summary>
        /// <param name="element">Element on which to set the prefix.</param>
        /// <param name="prefix">Prefix to set.</param>
        /// <param name="nameSpace">Namespace for which the prefix is intended.</param>
        public void SetNamespacePrefix(XElement element, string prefix, XNamespace nameSpace)
        {
            this.model.SetNamespacePrefix(element, prefix, nameSpace);
        }

        /// <summary>
        /// Expands the element.
        /// </summary>
        /// <param name="element">Element to expand.</param>
        /// <param name="address">Element's address.</param>
        /// <param name="range">Range to expand.</param>
        /// <param name="depth">Depth to expand.</param>
        public void Expand(XElement element, ElementAddress address, string range, int depth)
        {
            this.model.Expand(element, address, range, depth);
        }
        #endregion
    }
}